package com.example.compose

import androidx.compose.animation.VectorConverter
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.MutableTransitionState
import androidx.compose.animation.core.tween
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.requiredSize
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Path
import androidx.compose.ui.graphics.colorspace.ColorSpaces
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.launch

@Composable
fun YinYangHalf(
    modifier: Modifier = Modifier,
    diameter: Dp,
    sideColor: Color = Color.Black,
    dotColor: Color = Color.White,
    radiusBigDotProvider: () -> Float = { 0f },
    radiusSmallDotProvider: () -> Float = { 0f }
) {
    Canvas(modifier = modifier.requiredSize(diameter)) {
        val canvasSize = this.size.minDimension
        val center = Offset(x = canvasSize / 2, y = canvasSize / 2)
        val radius = canvasSize / 2
        val path = Path().apply {
            arcTo(
                Rect(
                    left = 0f, top = 0f, right = center.x + radius,
                    bottom = center.y + radius
                ),
                startAngleDegrees = 90f,
                sweepAngleDegrees = 180f,
                forceMoveTo = false
            )
            arcTo(
                rect = Rect(
                    center.x - radius / 2,
                    0f,
                    center.x + radius / 2,
                    center.y
                ),
                startAngleDegrees = 270f,
                sweepAngleDegrees = 180f,
                forceMoveTo = false
            )
            arcTo(
                Rect(
                    center.x - radius / 2,
                    center.y,
                    center.x + radius / 2,
                    center.y + radius
                ),
                270f,
                -180f,
                false
            )
        }
        drawPath(path, color = sideColor)
        val dotCenter = Offset(x = center.x, y = center.y - radius / 2)
        drawCircle(
            dotColor, center = dotCenter, radius = radiusBigDotProvider()
        )
        drawCircle(
            sideColor, center = dotCenter, radius = radiusSmallDotProvider()
        )
    }
}

@Composable
fun YinYang(
    modifier: Modifier = Modifier,
    diameter: Dp,
    yinModifier: Modifier = Modifier.graphicsLayer { rotationZ = 0f },
    yangModifier: Modifier = Modifier.graphicsLayer { rotationZ = 180f },
    radiusBigDotProvider: () -> Float = { 0f },
    radiusSmallDotProvider: () -> Float = { 0f }
) {
    Box(modifier = modifier) {
        YinYangHalf(
            diameter = diameter,
            modifier = yinModifier,
            sideColor = Color.White,
            dotColor = Color.Black,
            radiusBigDotProvider = radiusBigDotProvider,
            radiusSmallDotProvider = radiusSmallDotProvider
        )
        YinYangHalf(
            diameter = diameter,
            modifier = yangModifier,
            sideColor = Color.Black,
            dotColor = Color.White,
            radiusBigDotProvider = radiusBigDotProvider,
            radiusSmallDotProvider = radiusSmallDotProvider
        )
    }
}

@Composable
fun YinAndYang(
    modifier: Modifier = Modifier,
    diameter: Dp,
    yinModifier: Modifier = Modifier.graphicsLayer { rotationZ = 0f },
    yangModifier: Modifier = Modifier.graphicsLayer { rotationZ = 180f },
    radiusBigDotProvider: () -> Float = { 0f },
    radiusSmallDotProvider: () -> Float = { 0f }
) {
    Box(modifier = modifier) {
        YinYangHalf(
            diameter = diameter,
            modifier = yinModifier,
            sideColor = Color.White,
            dotColor = Color.Black,
            radiusBigDotProvider = radiusBigDotProvider,
            radiusSmallDotProvider = radiusSmallDotProvider
        )
        YinYangHalf(
            diameter = diameter,
            modifier = yangModifier,
            sideColor = Color.Black,
            dotColor = Color.White,
            radiusBigDotProvider = radiusBigDotProvider,
            radiusSmallDotProvider = radiusSmallDotProvider
        )
    }
}

enum class YinYangState {
    Yin, Yang
}

@Composable
fun YinAndYangScreen() {
    val diameter = 300.dp
    val pxSize = LocalDensity.current.run { diameter.toPx() }
    val radiusDotMax = pxSize / 4 + 1
    val radiusDotMin = pxSize / 20

    var yinYangState by remember {
        mutableStateOf(YinYangState.Yin)
    }
    val bgColor = remember {
        Animatable(
            initialValue = Color(0xff1e1f22),
            typeConverter = Color.VectorConverter(ColorSpaces.LinearSrgb)
        )
    }
    val angle = remember {
        Animatable(initialValue = 0f)
    }
    val radiusBigDot = remember {
        Animatable(initialValue = radiusDotMax)
    }
    val radiusSmallDot = remember {
        Animatable(initialValue = radiusDotMin)
    }

    val shouldTextBeVisible = remember {
        MutableTransitionState(initialState = false)
    }

    LaunchedEffect(key1 = yinYangState) {
        launch {
            shouldTextBeVisible.targetState = false
            angle.animateTo(
                targetValue = angle.value + 180f,
                animationSpec = tween(durationMillis = 3000)
            )
            shouldTextBeVisible.targetState = true
        }
        launch {
            bgColor.animateTo(
                targetValue = when (yinYangState) {
                    YinYangState.Yin -> Color.Black
                    YinYangState.Yang -> Color.White
                },
                animationSpec = tween(durationMillis = 3000)
            )
        }
        launch {
            radiusBigDot.animateTo(
                targetValue = if (yinYangState == YinYangState.Yang) radiusDotMax else radiusDotMin,
                animationSpec = tween(durationMillis = 3000)
            )
        }
        launch {
            radiusSmallDot.animateTo(
                targetValue = if (yinYangState == YinYangState.Yang) radiusDotMin else 0f,
                animationSpec = tween(durationMillis = 3000)
            )
        }
    }
    Column(
        modifier = Modifier
            .fillMaxSize()
            .drawBehind {
                drawRect(color = bgColor.value)
            },
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center
    ) {
        YinAndYang(
            diameter = diameter,
            modifier = Modifier
                .clickable {
                    yinYangState = when (yinYangState) {
                        YinYangState.Yin -> YinYangState.Yang
                        YinYangState.Yang -> YinYangState.Yin
                    }
                },
            yinModifier = Modifier.graphicsLayer { rotationZ = angle.value },
            yangModifier = Modifier.graphicsLayer { rotationZ = angle.value + 180f },
            radiusBigDotProvider = { radiusBigDot.value },
            radiusSmallDotProvider = { radiusSmallDot.value }
        )
    }
}


@Preview
@Composable
private fun YinYangPreview() {
    YinAndYangScreen()
}